//----------------------------------------------------------------------
//
// ECMAScript 2016 Polyfills
//
//----------------------------------------------------------------------

(function(global) {
	"use strict";
	var undefined = (void 0); // Paranoia

	// Helpers

	function isSymbol(s) {
		return(typeof s === 'symbol') || ('Symbol' in global && s instanceof global.Symbol);
	}

	function define(o, p, v, override) {
		if(p in o && !override)
			return;

		if(typeof v === 'function') {
			// Sanity check that functions are appropriately named (where possible)
			console.assert(isSymbol(p) || !('name' in v) || v.name === p || v.name === p + '_', 'Expected function name "' + p.toString() + '", was "' + v.name + '"');
			Object.defineProperty(o, p, {
				value: v,
				configurable: true,
				enumerable: false,
				writable: true
			});
		} else {
			Object.defineProperty(o, p, {
				value: v,
				configurable: false,
				enumerable: false,
				writable: false
			});
		}
	}

	// Snapshot intrinsic functions
	var $isNaN = global.isNaN;

	var abs = Math.abs,
		floor = Math.floor,
		max = Math.max,
		min = Math.min;

	//----------------------------------------
	// 7 Abstract Operations
	//----------------------------------------

	// 7.1.4
	function ToInteger(n) {
		n = Number(n);
		if($isNaN(n)) return 0;
		if(n === 0 || n === Infinity || n === -Infinity) return n;
		return((n < 0) ? -1 : 1) * floor(abs(n));
	}

	// 7.1.13 ToObject
	function ToObject(v) {
		if(v === null || v === undefined) throw TypeError();
		return Object(v);
	}

	// 7.1.15 ToLength ( argument )
	function ToLength(v) {
		var len = ToInteger(v);
		if(len <= 0) {
			return 0;
		}
		return min(len, 0x20000000000000 - 1); // 2^53-1
	}

	//----------------------------------------
	// 7.2 Testing and Comparison Operations
	//----------------------------------------

	// 7.2.10 SameValueZero(x, y)
	function SameValueZero(x, y) {
		if(typeof x !== typeof y) return false;
		switch(typeof x) {
			case 'undefined':
				return true;
			case 'number':
				if(x !== x && y !== y) return true;
				return x === y;
			case 'boolean':
			case 'string':
			case 'object':
			default:
				return x === y;
		}
	}

	//----------------------------------------------------------------------
	//
	// ECMAScript 2016
	//
	//----------------------------------------------------------------------

	// https://github.com/tc39/Array.prototype.includes/
	define(
		Array.prototype, 'includes',
		function includes(target) {
			var fromIndex = arguments[1];

			var o = ToObject(this);
			var len = ToLength(o["length"]);
			if(len === 0) return false;
			var n = ToInteger(fromIndex);
			if(n >= 0) {
				var k = n;
			} else {
				k = len + n;
				if(k < 0) k = 0;
			}
			while(k < len) {
				var elementK = o[k];
				if(SameValueZero(o[k], target))
					return true;
				k += 1;
			}
			return false;
		});

}(this));